<html>
<head><meta charset="utf-8"><title>RFC: Util functions to detect intermediate bindings · clippy · Zulip Chat Archive</title></head>
<h2>Stream: <a href="https://rust-lang.github.io/zulip_archive/stream/257328-clippy/index.html">clippy</a></h2>
<h3>Topic: <a href="https://rust-lang.github.io/zulip_archive/stream/257328-clippy/topic/RFC.3A.20Util.20functions.20to.20detect.20intermediate.20bindings.html">RFC: Util functions to detect intermediate bindings</a></h3>

<hr>

<base href="https://rust-lang.zulipchat.com">

<head><link href="https://rust-lang.github.io/zulip_archive/style.css" rel="stylesheet"></head>

<a name="225902256"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/257328-clippy/topic/RFC%3A%20Util%20functions%20to%20detect%20intermediate%20bindings/near/225902256" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Cameron Steffen <a href="https://rust-lang.github.io/zulip_archive/stream/257328-clippy/topic/RFC.3A.20Util.20functions.20to.20detect.20intermediate.20bindings.html#225902256">(Feb 10 2021 at 20:27)</a>:</h4>
<p>Oftentimes a lint does not work because the code has an intermediate binding between operations. For example, we often look for method call chains, but the code might put a <code>let x =</code> in between the method calls, and the lint would miss what happens. I have dreamed up some util functions to handle these cases. I want to request feedback to make sure these are easy to use and understand.</p>



<a name="225902367"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/257328-clippy/topic/RFC%3A%20Util%20functions%20to%20detect%20intermediate%20bindings/near/225902367" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Cameron Steffen <a href="https://rust-lang.github.io/zulip_archive/stream/257328-clippy/topic/RFC.3A.20Util.20functions.20to.20detect.20intermediate.20bindings.html#225902367">(Feb 10 2021 at 20:28)</a>:</h4>
<div class="codehilite" data-code-language="Rust"><pre><span></span><code><span class="sd">/// If the given expression is a local binding, find the initializer expression.</span>
<span class="sd">/// If that initializer expression is another local binding, find its initializer again.</span>
<span class="sd">/// This process repeats as long as possible (but usually no more than once). Initializer</span>
<span class="sd">/// expressions with adjustments are ignored. If this is not desired, use [find_binding_init]</span>
<span class="sd">/// instead.</span>
<span class="sd">///</span>
<span class="sd">/// Examples:</span>
<span class="sd">/// ```ignore</span>
<span class="sd">/// let abc = 1;</span>
<span class="sd">/// //        ^ output</span>
<span class="sd">/// let def = abc;</span>
<span class="sd">/// dbg!(def)</span>
<span class="sd">/// //   ^^^ input</span>
<span class="sd">///</span>
<span class="sd">/// // or...</span>
<span class="sd">/// let abc = 1;</span>
<span class="sd">/// let def = abc + 2;</span>
<span class="sd">/// //        ^^^^^^^ output</span>
<span class="sd">/// dbg!(def)</span>
<span class="sd">/// //   ^^^ input</span>
<span class="sd">/// ```</span>
<span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">expr_or_init</span><span class="o">&lt;'</span><span class="na">tcx</span><span class="o">&gt;</span><span class="p">(</span><span class="n">cx</span>: <span class="kp">&amp;</span><span class="nc">LateContext</span><span class="o">&lt;'</span><span class="na">tcx</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">expr</span>: <span class="kp">&amp;</span><span class="o">'</span><span class="na">tcx</span> <span class="nc">Expr</span><span class="o">&lt;'</span><span class="na">tcx</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kp">&amp;</span><span class="o">'</span><span class="na">tcx</span> <span class="nc">Expr</span><span class="o">&lt;'</span><span class="na">tcx</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">while</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">init</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">path_to_local</span><span class="p">(</span><span class="n">expr</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">.</span><span class="n">and_then</span><span class="p">(</span><span class="o">|</span><span class="n">id</span><span class="o">|</span><span class="w"> </span><span class="n">find_binding_init</span><span class="p">(</span><span class="n">cx</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">))</span><span class="w"></span>
<span class="w">        </span><span class="p">.</span><span class="n">filter</span><span class="p">(</span><span class="o">|</span><span class="n">init</span><span class="o">|</span><span class="w"> </span><span class="n">cx</span><span class="p">.</span><span class="n">typeck_results</span><span class="p">().</span><span class="n">expr_adjustments</span><span class="p">(</span><span class="n">init</span><span class="p">).</span><span class="n">is_empty</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="n">expr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">init</span><span class="p">;</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="n">expr</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="sd">/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.</span>
<span class="sd">/// By only considering immutable bindings, we guarantee that the returned expression represents the</span>
<span class="sd">/// value of the binding wherever it is referenced.</span>
<span class="sd">///</span>
<span class="sd">/// Example:</span>
<span class="sd">/// ```ignore</span>
<span class="sd">/// let abc = 1;</span>
<span class="sd">/// //        ^ output</span>
<span class="sd">/// dbg!(abc)</span>
<span class="sd">/// //   ^^^ input</span>
<span class="sd">/// ```</span>
<span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">find_binding_init</span><span class="o">&lt;'</span><span class="na">tcx</span><span class="o">&gt;</span><span class="p">(</span><span class="n">cx</span>: <span class="kp">&amp;</span><span class="nc">LateContext</span><span class="o">&lt;'</span><span class="na">tcx</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span><span class="n">hir_id</span>: <span class="nc">HirId</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Option</span><span class="o">&lt;&amp;'</span><span class="na">tcx</span><span class="w"> </span><span class="n">Expr</span><span class="o">&lt;'</span><span class="na">tcx</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="kd">let</span><span class="w"> </span><span class="n">hir</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cx</span><span class="p">.</span><span class="n">tcx</span><span class="p">.</span><span class="n">hir</span><span class="p">();</span><span class="w"></span>
<span class="w">    </span><span class="n">if_chain</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">Node</span>::<span class="n">Binding</span><span class="p">(</span><span class="n">pat</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">hir</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">hir_id</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="fm">matches!</span><span class="p">(</span><span class="n">pat</span><span class="p">.</span><span class="n">kind</span><span class="p">,</span><span class="w"> </span><span class="n">PatKind</span>::<span class="n">Binding</span><span class="p">(</span><span class="n">BindingAnnotation</span>::<span class="n">Unannotated</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="p">));</span><span class="w"></span>
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">Node</span>::<span class="n">Local</span><span class="p">(</span><span class="n">local</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">hir</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">hir</span><span class="p">.</span><span class="n">get_parent_node</span><span class="p">(</span><span class="n">hir_id</span><span class="p">))</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w">        </span><span class="n">then</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">            </span><span class="k">return</span><span class="w"> </span><span class="n">local</span><span class="p">.</span><span class="n">init</span><span class="p">;</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="nb">None</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>



<a name="225902668"></a>
<h4><a href="https://rust-lang.zulipchat.com#narrow/stream/257328-clippy/topic/RFC%3A%20Util%20functions%20to%20detect%20intermediate%20bindings/near/225902668" class="zl"><img src="https://rust-lang.github.io/zulip_archive/assets/img/zulip.svg" alt="view this post on Zulip" style="width:20px;height:20px;"></a> Cameron Steffen <a href="https://rust-lang.github.io/zulip_archive/stream/257328-clippy/topic/RFC.3A.20Util.20functions.20to.20detect.20intermediate.20bindings.html#225902668">(Feb 10 2021 at 20:30)</a>:</h4>
<p>Example use case: <a href="https://github.com/rust-lang/rust-clippy/pull/5375">https://github.com/rust-lang/rust-clippy/pull/5375</a></p>



<hr><p>Last updated: Aug 07 2021 at 22:04 UTC</p>
</html>